//
// Created by root on 2019/9/26.
//

#include "Phone.h"
#include <cstdio>
#include <cstring>

using namespace std;

const uint16_t PID_ANY_MATCH = 0x0;

static const vector<pair<uint16_t, uint16_t >> allPhone = {

        {0X22d9, PID_ANY_MATCH},// oppo
        {0x04e8, 0x6860},// samsung
        {0x2717, 0xff40},// xiaomi
        {0x2717, 0xff48},// xiaomi
        {0x12d1, 0x107e},// xiaomi
};


bool Phone::IsBelong(libusb_device *dev, int status) {
    uint16_t vid = 0;
    uint16_t pid = 0;
    int ret = 0;
    if (status == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
        return false;
    }
    libusb_device_descriptor deviceDescriptor;
    ret = libusb_get_device_descriptor(dev, &deviceDescriptor);
    if (ret < 0) {
        printf("failed to get device descriptor %s \n", libusb_error_name(ret));
        return false;
    }
    vid = deviceDescriptor.idVendor;
    pid = deviceDescriptor.idProduct;
    for (auto &vpid: allPhone) {
        if (vid == vpid.first && (vpid.second == pid || vpid.second == PID_ANY_MATCH)) {
            return true;
        }
    }
    return false;
}

const int AOA_PROTOCOL_MIN = 1;
const int AOA_PROTOCOL_MAX = 2;

typedef struct _accessory_t {
    char const *manufacturer;
    char const *model;
    char const *description;
    char const *version;
    char const *url;
    char const *serial;
} accessory_t;

static const accessory_t acc_default = {
        .manufacturer = "Google, Inc.",
        .model = "DemoKit",
        .description = "Demo ABS2013",
        .version = "2.0",
        .url = "https://github.com/gibsson",
        .serial = "0000000012345678",
};
#define AOA_STRING_MAN_ID        0
#define AOA_STRING_MOD_ID        1
#define AOA_STRING_DSC_ID        2
#define AOA_STRING_VER_ID        3
#define AOA_STRING_URL_ID        4
#define AOA_STRING_SER_ID        5


void Phone::handle(libusb_device *device, int status, AOAManager *manager) {
    int ret = 0;
    int r;
    unsigned char ioBuffer[2];
    libusb_device_handle *handle;
    int deviceProtocol;
    if (0 > libusb_open(device, &handle)) {
        printf("Failed to connect to device\n");
        return;
    }
    if (libusb_kernel_driver_active(handle, 0) > 0) {
        if (libusb_detach_kernel_driver(handle, 0) != 0) {
            printf("failed to detach kernel driver, ignoring device");
            libusb_close(handle);
            return;
        }
    }
    for (int i = 0; i < 3; ++i) {

        if (0 > (r = libusb_control_transfer(handle,
                                             0xC0, //bmRequestType
                                             51, //Get Protocol
                                             0,
                                             0,
                                             ioBuffer,
                                             2,
                                             2000))) {
            printf("get protocol call failed %s \n", libusb_error_name(r));
//            libusb_close(handle);
            sleep(1);
            continue;
        }
        break;
    }

    deviceProtocol = ioBuffer[1] << 8 | ioBuffer[0];
    if (deviceProtocol < AOA_PROTOCOL_MIN || deviceProtocol > AOA_PROTOCOL_MAX) {
//		logDebug("Unsupported AOADev protocol %d\n", deviceProtocol);
        printf("Unsupported AOADev protocol %d\n", deviceProtocol);
        libusb_close(handle);
        return;
    }
    uint8_t const AOA_SEND_IDENT = 52;
    printf("Support AOADEV protoal %d \n", deviceProtocol);
    printf(" sending manufacturer: %s\n", acc_default.manufacturer);
    ret = libusb_control_transfer(handle,
                                  LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR,
                                  AOA_SEND_IDENT,
                                  0,
                                  AOA_STRING_MAN_ID,
                                  (uint8_t *) acc_default.manufacturer,
                                  strlen(acc_default.manufacturer) + 1, 0);

    CHECKUSB(ret, "manufacturer");
    printf(" sending model: %s\n", acc_default.model);
    ret = libusb_control_transfer(handle,
                                  LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR,
                                  AOA_SEND_IDENT, 0,
                                  AOA_STRING_MOD_ID,
                                  (uint8_t *) acc_default.model,
                                  strlen(acc_default.model) + 1, 0);

    CHECKUSB(ret, "model");
    printf(" sending description: %s\n", acc_default.description);
    ret = libusb_control_transfer(handle,
                                  LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR,
                                  AOA_SEND_IDENT, 0, AOA_STRING_DSC_ID,
                                  (uint8_t *) acc_default.description,
                                  strlen(acc_default.description) + 1, 0);

    CHECKUSB(ret, "description");
    printf(" sending version: %s\n", acc_default.version);
    ret = libusb_control_transfer(handle,
                                  LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR,
                                  AOA_SEND_IDENT, 0, AOA_STRING_VER_ID,
                                  (uint8_t *) acc_default.version,
                                  strlen(acc_default.version) + 1, 0);

    CHECKUSB(ret, "version");
    printf(" sending url: %s\n", acc_default.url);
    ret = libusb_control_transfer(handle,
                                  LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR,
                                  AOA_SEND_IDENT,
                                  0,
                                  AOA_STRING_URL_ID,
                                  (uint8_t *) acc_default.url,
                                  strlen(acc_default.url) + 1, 0);
    CHECKUSB(ret, "url");
    printf(" sending serial number: %s\n", acc_default.serial);
    ret = libusb_control_transfer(handle,
                                  LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR,
                                  AOA_SEND_IDENT,
                                  0, AOA_STRING_SER_ID,
                                  (uint8_t *) acc_default.serial,
                                  strlen(acc_default.serial) + 1, 0);

    CHECKUSB(ret, "serial");
    if (deviceProtocol >= 2) {
        ret = libusb_control_transfer(handle,
                                      0x40,
                                      58,
                                      1, // 0=no audio, 1=2ch,16bit PCM, 44.1kHz
                                      0,
                                      NULL,
                                      0,
                                      2000);
        if (ret < 0) {
            printf("set audio call failed\n");
            goto out;
        }
    }

    ret = libusb_control_transfer(handle,
                                  0x40,
                                  53,
                                  0,
                                  0,
                                  NULL,
                                  0,
                                  2000);
    if (ret < 0) {
        printf("start accessory call failed\n");
        goto out;
    }
    out:
    libusb_close(handle);
}

